# Created by 黄景涛
# DATE: 2024/12/30
import builtins
import json
import os
import re
from yaml import safe_load as yaml_load, dump as yaml_dump
from config.settings import cfg, ROOT_PATH
from utils.common_tools import hjt_tool_3


class VarProcess:
    get_mode = ('pool', 'function')
    # 转义表 元组[0]是要替换的字符串，元组[1]是替换后的字符串
    # 变量回写至池内时会由1替换到0；最终结果返回前会由0替换到1
    CONVERT_TABLE = [
        ('[[d]]', '$'),
        ('[[ob]]', '{'),
        ('[[cb]]', '}'),
        ('[[op]]', '('),
        ('[[cp]]', ')'),
        ('[[cm]]', ','),
        ('[[n]]', '\n'),
    ]
    custom_functions = hjt_tool_3
    env = cfg.get('env', None)
    pool_file = os.path.join(ROOT_PATH, f'config/{env}_var_pool.yaml')

    def __init__(self, test_instance):
        self.test_cls_name = f"{test_instance[0].__class__.__name__}"
        self.seq_number = test_instance[1]

    @classmethod
    def is_var(cls, s: str):
        """
        检查是否是变量
        :param s: 待检查项
        :return:
        """
        if not isinstance(s, str):
            return False
        return re.search(r'\$\{.*?}', s.strip()) is not None

    @classmethod
    def convert(cls, s: str, rev = False):
        """
        按转义表转义字符串
        :param s: 待转换项
        :param rev: 转换方向 默认False从左到右，True从右到左
        :return:
        """
        if isinstance(s, str):
            if rev:
                # 从右到左
                for L, R in cls.CONVERT_TABLE:
                    s = s.replace(R, L)
            else:
                # 从左到右
                for L, R in cls.CONVERT_TABLE:
                    s = s.replace(L, R)
        return s

    def get_var_value(self, var_name: str, *args):
        """
        获取变量值
        :param var_name: 变量名
        :param args: 参数
        :return:
        """
        get_mode = self.get_mode[0]
        with open(self.pool_file, mode='r', encoding='utf8') as f:
            var_pool = yaml_load(f)
        var_value = var_pool.get(var_name)

        if var_value is None:
            func = getattr(self.custom_functions, var_name, None)
            if func is not None:
                var_value = func(*args)
            else:
                func = getattr(__builtins__, var_name, None)
                if func is not None:
                    var_value = func(*args)
            get_mode = self.get_mode[1]

        return var_value, get_mode

    def write_back_var(self, var_value: str):
        """
        回写变量，把前面步骤生成的变量保存到pool中
        :param var_value: 解析后的变量值
        :return:
        """
        with open(self.pool_file, mode='r', encoding='utf8') as f:
            pool = yaml_load(f)
        var_k = f'{self.test_cls_name}.{self.seq_number}'
        pool[var_k] = self.convert(var_value, True)
        with open(self.pool_file, mode='w', encoding='utf8') as f:
            yaml_dump(pool, f, allow_unicode=True)

    def parse_args(self, args_str):
        """
        解析函数参数列表
        :param args_str: 参数字符串，例如 "B${C(D,E)}F, G"
        :return: 解析后的参数列表，是否存在嵌套
        """
        inner_used_func = False
        args = []
        stack = []
        current_arg = []
        result = list(args_str)
        i = 0
        while i < len(result):
            if ''.join(result[i: i+2]) == "${":
                stack.append(i)
                i += 2  # 跳过"${"
            elif result[i] == "}" and stack:
                start = stack.pop()
                # 处理${}内的部分
                expr = result[start: i+1]  # 获取整个嵌套表达式
                value, inner_used_func = self.replace_recursive(expr)
                result = [*result[:start], *value, *result[i+1:]]
                # 重新从完成替换的位置开始处理
                i = start
            elif result[i] == ',' and not stack:
                args.append(''.join(current_arg).strip())
                current_arg.clear()
                i += 1
            else:
                current_arg.append(result[i])
                i += 1
        if current_arg:
            args.append(''.join(current_arg).strip())
        return args, inner_used_func

    def parse_expression(self, expr: str):
        """
        解析表达式，获取变量名及其参数
        :param expr: 表达式字符串，例如 "A(B${C(D,E)}F, G)"
        :return: 函数名 参数 参数内是否存在函数调用
        """
        # 解析变量名
        pattern = r"([\w.]+)"
        match = re.match(pattern, expr)
        if not match:
            return expr, [], False
        var_name = match.group(1)
        args_str = expr[len(var_name)+1:].strip()
        if len(args_str) > 1 and args_str[-1] == ')':
            args_str = args_str[:-1].strip()
        args, inner_used_func = self.parse_args(args_str)
        return var_name, args, inner_used_func

    # 使用栈来处理嵌套的${}表达式
    def replace_recursive(self, s):
        used_func = False
        stack = []
        result = list(s)  # 将字符串转换为列表，方便修改
        i = 0
        while i < len(result):
            if ''.join(result[i : i+2]) == "${":
                stack.append(i)
                i += 2  # 跳过 "${"
            elif result[i] == "}" and stack:
                start = stack.pop()
                # 处理${}内的部分
                expr = ''.join(result[start+2 : i])  # 获取表达式内容
                var_name, args, inner_used_func = self.parse_expression(expr)
                value, get_mode = self.get_var_value(var_name, *args)
                if inner_used_func or (get_mode == self.get_mode[1]):
                    used_func = True
                # 替换整个${}表达式
                result = [*result[:start], *value, *result[i+1:]]
                # 重新从完成替换的位置开始处理
                i = start
            else:
                i += 1  # 正常字符跳过
        return ''.join(result), used_func

    def sub(self, str_with_var: str) -> str:
        """
        变量替换
        :param str_with_var: 含有变量占位符的表达式
        :return:
        """
        if self.is_var(str_with_var):
            str_with_var, used_function = self.replace_recursive(str_with_var)
            # 若存在函数调用，回写替换完成的结果
            if used_function:
                self.write_back_var(str_with_var)
        return self.convert(str_with_var)



if __name__ == '__main__':
    var = VarProcess(('abc', 1))
    print(var.sub('//span[contains(text(), "${host_asst_model1}")]'))
